package com.spikegpsongoogleappengine;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;

public class MyLocationOverlay extends ItemizedOverlay<OverlayItem> implements
		LocationListener {

	private static final String TAG = "MyLocationOverlay: ";
	private LocationManager mLocationManager;
	private long updateTime = 60000;
	private static final int updateDistance = 50;
	private GeoPoint lastKnownPoint;
	private Location lastKnownLocation;
	private Drawable centerDrawable;
	private Context mContext;
	private final List<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
	private Paint accuracyPaint;
	private Point center;
	private Point left;
	private Drawable drawable;
	private int width;
	private int height;
	private final static double[] DEFAULT_LOCATION = { 37.7749295, -122.4194155 };
	private static final boolean DEBUG = true;
	private Runnable firstFixRunnable = null;
	private boolean firstFixRun = false;

	public MyLocationOverlay(Drawable defaultMarker, MapView mapView, Context c) {
		super(boundCenter(defaultMarker));
		this.centerDrawable = defaultMarker;
		this.mContext = c;
		mLocationManager = (LocationManager) c
				.getSystemService(Context.LOCATION_SERVICE);

		if (DEBUG) {
			updateTime = 0;
		} else {
			updateTime = 60000;
		}
	}

	public void addOverlay(OverlayItem overlay) {
		mOverlays.add(overlay);
		populate();
	}

	private void checkFirstRunnable() {
		if (!firstFixRun && lastKnownLocation != null
				&& firstFixRunnable != null) {
			firstFixRunnable.run();
		}
	}

	private OverlayItem createCenterOverlay(GeoPoint point) {
		OverlayItem i = new OverlayItem(point, "Location", null);
		i.setMarker(centerDrawable);
		return i;
	}

	private GeoPoint createGeoPoint(Location loc) {
		int lat = (int) (loc.getLatitude() * 1E6);
		int lng = (int) (loc.getLongitude() * 1E6);
		return new GeoPoint(lat, lng);
	}

	@Override
	protected OverlayItem createItem(int i) {
		return mOverlays.get(i);
	}

	public void disableLocation() {
		mLocationManager.removeUpdates(this);
	}

	@Override
	public void draw(Canvas canvas, MapView mapView, boolean shadow) {
		drawMyLocation(canvas, mapView, lastKnownLocation, lastKnownPoint, 0);
	}

	protected void drawMyLocation(Canvas canvas, MapView mapView,
			Location lastFix, GeoPoint myLoc, long when) {

		accuracyPaint = new Paint();
		accuracyPaint.setAntiAlias(true);
		accuracyPaint.setStrokeWidth(2.0f);

		drawable = centerDrawable;
		width = drawable.getIntrinsicWidth();
		height = drawable.getIntrinsicHeight();
		center = new Point();
		left = new Point();

		Projection projection = mapView.getProjection();

		double latitude = lastFix.getLatitude();
		double longitude = lastFix.getLongitude();
		float accuracy = lastFix.getAccuracy();

		float[] result = new float[1];

		Location.distanceBetween(latitude, longitude, latitude, longitude + 1,
				result);
		float longitudeLineDistance = result[0];

		GeoPoint leftGeo = new GeoPoint((int) (latitude * 1e6),
				(int) ((longitude - accuracy / longitudeLineDistance) * 1e6));
		projection.toPixels(leftGeo, left);
		projection.toPixels(myLoc, center);
		int radius = center.x - left.x;

		accuracyPaint.setColor(0xff6666ff);
		accuracyPaint.setStyle(Style.STROKE);
		canvas.drawCircle(center.x, center.y, radius, accuracyPaint);

		accuracyPaint.setColor(0x186666ff);
		accuracyPaint.setStyle(Style.FILL);
		canvas.drawCircle(center.x, center.y, radius, accuracyPaint);

		drawable.setBounds(center.x - width / 2, center.y - height / 2,
				center.x + width / 2, center.y + height / 2);
		drawable.draw(canvas);
	}

	public void enableMyLocation() {
		for (String s : mLocationManager.getProviders(true)) {
			mLocationManager.requestLocationUpdates(s, updateTime,
					updateDistance, this);
		}

		Location loc = null;

		for (String s : mLocationManager.getProviders(true)) {
			loc = mLocationManager.getLastKnownLocation(s);
			if (loc != null) {
				loc.setLatitude(DEFAULT_LOCATION[0]);
				loc.setLongitude(DEFAULT_LOCATION[1]);
				lastKnownLocation = loc;
				lastKnownPoint = createGeoPoint(loc);
				return;
			}
		}

		loc = new Location(LocationManager.GPS_PROVIDER);
		loc.setLatitude(DEFAULT_LOCATION[0]);
		loc.setLongitude(DEFAULT_LOCATION[1]);

		lastKnownLocation = loc;
		lastKnownPoint = createGeoPoint(loc);
	}

	public Location getLastKnownLocation() {
		return lastKnownLocation;
	}

	public GeoPoint getLastKnownPoint() {
		return lastKnownPoint;
	}

	public void onLocationChanged(Location location) {
		checkFirstRunnable();
		this.lastKnownLocation = location;
		this.lastKnownPoint = createGeoPoint(location);
		replaceOverlay(createCenterOverlay(lastKnownPoint));

	}

	public void onProviderDisabled(String provider) {
		Log.w(TAG,
				"Your location provider has been disabled -- please reenable it");

	}

	public void onProviderEnabled(String provider) {

	}

	public void onStatusChanged(String provider, int status, Bundle extras) {

		if (status == LocationProvider.AVAILABLE) {

		}
		if (status == LocationProvider.OUT_OF_SERVICE) {
			Log.w(TAG, "Location is temporarily out of service.");
		}
		if (status == LocationProvider.TEMPORARILY_UNAVAILABLE) {

		}
	}

	private void replaceOverlay(OverlayItem overlay) {
		mOverlays.clear();
		mOverlays.add(overlay);
		populate();
	}

	public boolean runOnFirstFix(Runnable runnable) {
		if (lastKnownLocation != null) {
			runnable.run();
			return true;
		}

		firstFixRunnable = runnable;
		return false;
	}

	@Override
	public int size() {
		return mOverlays.size();
	}

}